 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UsbBitWacker;
using NDde.Server;

namespace UbwApplication
{
    public class UbwDdeServer : DdeServer
    {
        private System.Timers.Timer _Timer = new System.Timers.Timer();
        private UBW ubw = null;

        public UbwDdeServer(string service, UBW ubw) : base(service)
        {
            this.ubw = ubw;

            // Create a timer that will be used to advise clients of new data.
            _Timer.Elapsed += new System.Timers.ElapsedEventHandler(_Timer_Elapsed);
            _Timer.Interval = 1000;
            _Timer.SynchronizingObject = this.Context;
        }

        public UbwDdeServer(UBW ubw)
            : this("ubw", ubw)
        { }

        void _Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            // Advise all topic name and item name pairs.
            Advise("*", "*");
        }
        
        public override void Register()
        {
            base.Register();
            _Timer.Start();
        }

        public override void Unregister()
        {
            _Timer.Stop();
            base.Unregister();
        }

        protected override bool OnBeforeConnect(string topic)
        {
            Console.WriteLine("OnBeforeConnect:".PadRight(16)
                + " Service='" + base.Service + "'"
                + " Topic='" + topic + "'");

            return true;
        }

        protected override void OnAfterConnect(DdeConversation conversation)
        {
            Console.WriteLine("OnAfterConnect:".PadRight(16)
                + " Service='" + conversation.Service + "'"
                + " Topic='" + conversation.Topic + "'"
                + " Handle=" + conversation.Handle.ToString());
        }

        protected override void OnDisconnect(DdeConversation conversation)
        {
            Console.WriteLine("OnDisconnect:".PadRight(16)
                + " Service='" + conversation.Service + "'"
                + " Topic='" + conversation.Topic + "'"
                + " Handle=" + conversation.Handle.ToString());
        }

        protected override bool OnStartAdvise(DdeConversation conversation, string item, int format)
        {
            Console.WriteLine("OnStartAdvise:".PadRight(16)
                + " Service='" + conversation.Service + "'"
                + " Topic='" + conversation.Topic + "'"
                + " Handle=" + conversation.Handle.ToString()
                + " Item='" + item + "'"
                + " Format=" + format.ToString());

            // Initiate the advisory loop only if the format is CF_TEXT.
            return format == 1;
        }

        protected override void OnStopAdvise(DdeConversation conversation, string item)
        {
            Console.WriteLine("OnStopAdvise:".PadRight(16)
                + " Service='" + conversation.Service + "'"
                + " Topic='" + conversation.Topic + "'"
                + " Handle=" + conversation.Handle.ToString()
                + " Item='" + item + "'");
        }

        protected override ExecuteResult OnExecute(DdeConversation conversation, string command)
        {
            Console.WriteLine("OnExecute:".PadRight(16)
                + " Service='" + conversation.Service + "'"
                + " Topic='" + conversation.Topic + "'"
                + " Handle=" + conversation.Handle.ToString()
                + " Command='" + command + "'");

            // Tell the client that the command was processed.
            return ExecuteResult.Processed;
        }

        protected override PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format)
        {
            try
            {
                Console.WriteLine("OnPoke:".PadRight(16)
                        + " Service='" + conversation.Service + "'"
                        + " Topic='" + conversation.Topic + "'"
                        + " Handle=" + conversation.Handle.ToString()
                        + " Item='" + item + "'"
                        + " Data=" + data.Length.ToString()
                        + " Format=" + format.ToString());

                if (format == 1)
                {
                    if (conversation.Topic.ToLower() == "digital")
                    {
                        string port = "";
                        int pin = -1;
                        int value = -1;

                        // item should be format "A1". Port = A, Pin = 1
                        parsePortPin(item, out port, out pin);
                        if (pin >= 0)
                        {
                            if ((data.Length == 2) && (data[1] == '\0'))
                            {
                                if (data[0] == (byte)'0')
                                    value = 0;
                                else if (data[0] == (byte)'1')
                                    value = 1;
                            }
                            if ((pin >= 0) && (value >= 0))
                            {
                                try
                                {
                                    ubw.WriteDigitalPin(port, pin, value);
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine(ex.ToString());
                                }
                                return PokeResult.Processed;
                            }
                        }
                    }
                    else if (conversation.Topic.ToLower() == "direction")
                    {
                        string port = "";
                        int pin = -1;
                        string direction = "";

                        // item should be format "A1". Port = A, Pin = 1
                        parsePortPin(item, out port, out pin);
                        if (pin >= 0)
                        {
                            try
                            {
                                direction = UTF8Encoding.UTF8.GetString(data).ToLower();

                                if (direction.StartsWith("input"))
                                {
                                    ubw.ConfigurePinAsInput(port, pin);
                                }
                                else if (direction.StartsWith("output"))
                                {
                                    ubw.ConfigurePinAsOutput(port, pin);
                                }
                            }
                            catch (Exception ex)
                            {
                                // ignore exceptions
                                Console.WriteLine(ex.ToString());
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            // Tell the client that the data was processed.
            return PokeResult.Processed;
        }

        protected override RequestResult OnRequest(DdeConversation conversation, string item, int format)
        {
            try
            {
                Console.WriteLine("OnRequest:".PadRight(16)
                        + " Service='" + conversation.Service + "'"
                        + " Topic='" + conversation.Topic + "'"
                        + " Handle=" + conversation.Handle.ToString()
                        + " Item='" + item + "'"
                        + " Format=" + format.ToString());

                // Return data to the client only if the format is CF_TEXT.
                if (format == 1)
                {
                    if (conversation.Topic.ToLower() == "analog")
                    {
                        int channel = -1;
                        int value = -1;

                        int.TryParse(item, out channel);

                        if (channel >= 0)
                        {
                            try
                            {
                                value = ubw.ReadAnalogInput(channel);
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.ToString());
                            }

                            return newRequestResult(value.ToString());
                        }
                    }
                    else if (conversation.Topic.ToLower() == "digital")
                    {
                        string port = "";
                        int pin = -1;
                        int value = 0;

                        // item should be format "A1". Port = A, Pin = 1
                        parsePortPin(item, out port, out pin);
                        if (pin >= 0)
                        {
                            try
                            {
                                value = ubw.ReadDigitalPin(port, pin);
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.ToString());
                            }
                            return newRequestResult(value.ToString());
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            return RequestResult.NotProcessed;
        }

        private void parsePortPin(string portPinString, out string port, out int pin)
        {
            port = null;
            pin = -1;

            if (portPinString.Length == 2)
            {
                port = portPinString.Substring(0, 1).ToUpper();
                int.TryParse(portPinString.Substring(1), out pin);
            }
        }

        protected override byte[] OnAdvise(string topic, string item, int format)
        {
            Console.WriteLine("OnAdvise:".PadRight(16)
                + " Service='" + this.Service + "'"
                + " Topic='" + topic + "'"
                + " Item='" + item + "'"
                + " Format=" + format.ToString());

            // Send data to the client only if the format is CF_TEXT.
            if (format == 1)
            {
                return System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + "\0");
            }
            return null;
        }

        private RequestResult newRequestResult(string result)
        {
            return new RequestResult(System.Text.Encoding.ASCII.GetBytes(result + "\0"));
        }
    }
}
